#include "user_info.h"
#include "debug.h"
#include "error.h"
#include "logging.h"
#include "utils.h"

namespace user_info
{
	HRESULT GetProcessUser(CString* name, CString* domain, CString* sid) {
		CSid current_sid;

		HRESULT hr = GetProcessUserSid(&current_sid);
		if (FAILED(hr)) {
			return hr;
		}

		if (sid != NULL) {
			*sid = current_sid.Sid();
		}
		if (name != NULL) {
			*name = current_sid.AccountName();
		}
		if (domain != NULL) {
			*domain = current_sid.Domain();
		}
		return S_OK;
	}

	HRESULT GetProcessUserSid(CSid* sid) {
		ASSERT1(sid);

		CAccessToken token;
		if (!token.GetProcessToken(TOKEN_QUERY) || !token.GetUser(sid)) {
			HRESULT hr = HRESULTFromLastError();

			// Assert only if thread_sid is populated. This is to eliminate other
			// reasons for GetProcessToken/GetUser to fail.
			CString thread_sid;
			ASSERT(FAILED(GetThreadUserSid(&thread_sid)),
				(_T("[Did you mean to call GetThreadUserSid?][0x%x][%s]"),
				hr, thread_sid));

			return hr;
		}

		return S_OK;
	}

	HRESULT IsLocalSystemUser(bool* is_local_system, CString* user_sid) {
		ASSERT1(is_local_system);

		CString sid;
		HRESULT hr = GetProcessUser(NULL, NULL, &sid);
		if (FAILED(hr)) {
			return hr;
		}
		*is_local_system = sid.CompareNoCase(kLocalSystemSid) == 0;
		if (user_sid) {
			user_sid->SetString(sid);
		}
		return S_OK;
	}

	HRESULT GetThreadUserSid(CString* sid) {
		ASSERT1(sid);
		CAccessToken access_token;
		CSid user_sid;
		if (access_token.GetThreadToken(TOKEN_READ) &&
			access_token.GetUser(&user_sid)) {
				sid->SetString(user_sid.Sid());
				return S_OK;
		} else {
			HRESULT hr = HRESULTFromLastError();
			UTIL_LOG(L2, (_T("[GetThreadUserSid failed][0x%x]"), hr));
			return hr;
		}
	}

	HRESULT GetEffectiveUserSid(CString* sid) {
		HRESULT hr = GetThreadUserSid(sid);
		return SUCCEEDED(hr) ? hr : GetProcessUser(NULL, NULL, sid);
	}

	bool IsRunningAsSystem() {
		CString sid;
		return SUCCEEDED(GetEffectiveUserSid(&sid)) ? IsLocalSystemSid(sid) : false;
	}

	bool IsThreadImpersonating() {
		CAccessToken access_token;
		return access_token.GetThreadToken(TOKEN_READ);
	}
}	// namespace user_info